from sqlalchemy.orm        import MapperExtension, EXT_PASS
import formencode
from formencode import Invalid

### FormEncode validators
class RangeValidator(formencode.FancyValidator):
    def __init__(self, min, max, *args, **kwargs):
        self.min = min
        self.max = max
    
    def validate_python(self, value, state):
        if not (self.min <= value <= self.max):
            raise formencode.Invalid('Invalid range', value, state)

class RegexValidator(formencode.validators.Regex):
    def is_empty(self, value):
        return False

### SQLAlchemy mapper extension
class Validator(MapperExtension):
    def __init__(self, *args):
        MapperExtension.__init__(self)
        self.fe_schema = formencode.Schema(allow_extra_fields = True)

        for validation in args:
            self.add_validation(validation)
    
    def add_validation(self, validation):
        if validation['type'] == 'field':
            self.fe_schema.add_field(validation['field'], validation['validator'])
        else:
            self.fe_schema.add_chained_validator(validation['validator'])
    
    def before_insert(self, mapper, connection, instance):
        self.validate(instance)
        return EXT_PASS
    
    def validate(self, instance):
        self.fe_schema.to_python(instance.__dict__)
    
    before_update = before_insert

### SQLAlchemy/Elixir functions/statements

# Generic formencode validators
def formencode_validate_field(field_to_validate, formencode_validator):
    return {
        'type'     : 'field',
        'field'    : field_to_validate,
        'validator': formencode_validator
    }

def formencode_validate_instance(formencode_validator):
    return {
        'type'     : 'instance',
        'validator': formencode_validator
    }

# Rails-like validators
def range_of(field_to_validate, min, max, *args, **kwargs):
    return formencode_validate_field(field_to_validate, RangeValidator(min, max, *args, **kwargs))

def format_of(field_to_validate, *args, **kwargs):
    return formencode_validate_field(field_to_validate, RegexValidator(*args, **kwargs))

def presence_of(*args, **kwargs):
    if not kwargs.has_key('not_empty'):
        kwargs['not_empty'] =  True
    
    for field_to_validate in args:
        return formencode_validate_field(field_to_validate, formencode.FancyValidator(**kwargs))

__all__ = ['Validator', 'range_of', 'format_of', 'presence_of', 'formencode_validate_field',
           'formencode_validate_instance', 'Invalid']